<?php

namespace yphp;

/**
 * Class Base
 * @package yphp
 * 数据库基类
 */
abstract class DbBase extends Instance
{
    protected $query;

    final protected function componet($table, $dth)
    {
        $this->query = Query::instance($table, $dth);
        return $this;
    }

    // 魔术方法
    function __call($name, $args)
    {
        if (!method_exists($this->query, $name)) throw new \Exception(get_called_class() . '对象不存在' . $name . '方法');
        $return = call_user_func_array([$this->query, $name], $args);
        return substr($name, -3) === 'Sql' ? $return : $this;
    }

    // 指定库名
    final protected static function setDbn($address, $dbn)
    {
        $linkInfo = self::linkInfo($address);
        $link = $linkInfo['link'];
        if ($dbn !== $linkInfo['dbn']) {
            if (!in_array($dbn, self::dbName($address))) throw new \Exception('服务器 ' . $address . ' 不存在数据库 ' . $dbn);
            $link->query(" USE `$dbn` ");
            self::$linkInfo[$address]['dbn'] = $dbn;
        }
        return $link;
    }

    // 连接列表：连接、当前库
    private static $linkInfo = [];

    // 获取连接信息
    final protected static function linkInfo($address)
    {
        return key_exists($address, self::$linkInfo) ? self::$linkInfo[$address] : self::$linkInfo[$address] = call_user_func(function () use ($address) {
            $node = Config::databaseSpot('link', $address);
            list($host, $port) = explode(':', $address);
            return ['link' => new \PDO("mysql:host=$host;port=$port", $node['du'], $node['dp']), 'dbn' => null];
        });
    }

    // 连接库名列表
    private static $dbNames = [];

    final protected static function dbName($address)
    {
        return key_exists($address, self::$dbNames) ? self::$dbNames[$address] : self::$dbNames[$address] = call_user_func(function () use ($address) {
            $dbNames = [];
            foreach (self::inQuery(self::linkInfo($address)['link'], " SHOW DATABASES ") as $v) {
                $dbNames[] = $v->Database;
            }
            return $dbNames;
        });
    }

    // 表结构
    private static $describe = [];

    final static function describe($address, $dbn, $tbn)
    {
        return empty(self::$describe[$address][$dbn][``]) ? self::$describe[$address][$dbn][$tbn] = call_user_func(function () use ($address, $dbn, $tbn) {
            $desc = new \stdClass();
            $desc->list = new \stdClass();
            $desc->primaryKey = null;
            foreach (self::inQuery(self::setDbn($address, $dbn), " DESC `$tbn` ") as $v) {
                $v->Key === 'PRI' && $desc->primaryKey = $v->Field;
                $desc->list->{$v->Field} = call_user_func(function ($row) {
                    unset($row->Field);
                    return $row;
                }, $v);
            }
            return $desc;
        }) : self::$describe[$address][$dbn][$tbn];
    }

    // 字段值处理
    final static function parseValue($structure, $val = null)
    {
        switch (explode('(', $structure->Type)[0]) {
            case 'char':
            case 'varchar':
            case 'tinytext':
            case 'longtext':
            case 'mediumtext':
            case 'text':
                $val = is_null($val) ? $structure->Default : (is_array($val) ? json_encode($val) : (string)$val);
                break;
            case 'tinyint':
            case 'smallint':
            case 'mediumint':
            case 'int':
            case 'bigint':
                $val = is_null($val) ? $structure->Default : (int)$val;
                break;
            default:
                $val = is_null($val) ? $structure->Default : floatval($val);
                break;
        }
        return (is_null($val) && !is_null($structure->Default)) ? $structure->Default : $val;
    }

    // 表SQL封装：查询、增删改
    private static $lastSql;

    final static function lastSql($real = false)
    {
        return $real ? self::realSql(self::$lastSql) : self::$lastSql;
    }

    final static function realSql($sqls)
    {
        list($sql, $bind) = $sqls;
        if (!$bind) return $sql;
        foreach ($bind as $key => $value) {
            $sql = str_replace($key, is_string($value) ? '"'. $value . '"' : $value, $sql);
        }
        return $sql;
    }



    final static function inQuery($link, $sql, $bind = [], $rArr = false)
    {
        self::$lastSql = [$sql, $bind];
        $sth = $link->prepare($sql);
        $sth->execute($bind);
        $sth->setFetchMode($rArr ? \PDO::FETCH_ASSOC : \PDO::FETCH_OBJ);
        $res = [];
        while ($v = $sth->fetch()) {
            $res[] = $v;
        }
        $sth = null;
        return $res;
    }

    final static function modify($link, $sql, $bind = [], $insert = false)
    {
        self::$lastSql = [$sql, $bind];
        $bool = $link->prepare($sql)->execute($bind);
        return $insert ? $bool ? $link->lastInsertId() : 0 : $bool;
    }

    // 查单行
    final function find($rArr = false)
    {
        $this->limitOne();
        $rm = new \ReflectionMethod($this, 'select');
        $res = $rm->invokeArgs($this, [$rArr]);
        return current($res) ?: [];
    }

    // 查单字段值
    final function one($field = null)
    {
        is_null($field) || $this->field($field);
        $find = $this->find(\PDO::FETCH_OBJ);
        return current($find) ?: null;
    }

    // 分页
    final function pagination($num = null)
    {
        return Page::instance($this,$num);
    }

}